﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
using Cartoons.Controls;
using System.Windows.Data;

namespace Cartoons
{
    public partial class DeepZoomBook : UserControl
    {
        private const double pageSize = 6;
        private readonly Point hideDestinationPoint = new Point(100, 100);
        public DeepZoomBook()
        {
            InitializeComponent();

            msiBook.ImageOpenSucceeded += msiBook_ImageOpenSucceeded;
            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (!(BroadsidesCount > 0))
                return;

            var shadowRect = new Rect();

            if (Broadside < 1)
            {
                shadowRect = GetSubImageRect(1);
                if (shadowRect.X < 0)
                    shadowRect = GetSubImageRect(3);

                if (Broadside > 0.5)
                {
                    var k = (Broadside - 0.5) * 2;
                    k = 1 - Math.Pow(1 - k, 4);
                    shadowRect.X -= shadowRect.Width * k;
                    shadowRect.Width *= (1 + k);
                }
            }
            else if (Broadside < BroadsidesCount - 1 || msiBook.SubImages.Count % 2 == 0)
            {
                shadowRect = GetSubImageRect(SelectedBroadside * 2);
                shadowRect.Width *= 2;
            }
            else
            {
                shadowRect = GetSubImageRect(SelectedBroadside);
            }

            shadow.Width = shadowRect.Width;
            shadow.Height = shadowRect.Height;
            shadow.SetValue(Canvas.LeftProperty, shadowRect.X - 5);
            shadow.SetValue(Canvas.TopProperty, shadowRect.Y + 5);

        }

        private Rect GetSubImageRect(int nSubImageIndex)
        {
            MultiScaleSubImage subImage = msiBook.SubImages[nSubImageIndex];
            Point topLeft = subImage.ViewportOrigin;
            topLeft.X = -(topLeft.X / subImage.ViewportWidth);
            topLeft.Y = -(topLeft.Y / subImage.ViewportWidth);
            double width = 1 / subImage.ViewportWidth;
            double height = width / subImage.AspectRatio;
            Point bottomright = new Point(topLeft.X + width, topLeft.Y + height);
            topLeft = msiBook.LogicalToElementPoint(topLeft);
            bottomright = msiBook.LogicalToElementPoint(bottomright);
            return new Rect(topLeft, bottomright);
        }
        #region Book source selection

        void msiBook_ImageOpenSucceeded(object sender, RoutedEventArgs e)
        {
            foreach (var page in msiBook.SubImages)
                page.ViewportOrigin = hideDestinationPoint;
            Broadside = 0;
            ChangeSelectedBroadside(0, 0);
        }

        private void ChangeBookSource(Uri uri)
        {
            msiBook.Source = new DeepZoomImageTileSource(uri);
        }

        #endregion

        #region Paging

        private void CreatePageScreenshot()
        {
            PagingFactor = 1;
            imgBrush.ImageSource = new WriteableBitmap(bdr, null);
        }

        private void FixPage(int newPage)
        {
            SelectedBroadside = newPage;
            PagingFactor = 1;
        }

        private bool ChangeSelectedBroadside(int oldPage, int newPage)
        {
            if (newPage > this.BroadsidesCount || newPage < 0)
                return false;

            if (newPage == 1 || oldPage == 0)
                effect.K = 0;
            else
                effect.ClearValue(PageListEffect.KProperty);

            foreach (var page in msiBook.SubImages.Skip(oldPage * 2).Take(2))
                page.ViewportOrigin = hideDestinationPoint;

            var selectedPages = msiBook.SubImages.Skip(newPage * 2).Take(2).ToArray();

            selectedPages[0].ViewportWidth = pageSize;

            selectedPages[0].ViewportOrigin = new Point(-pageSize / 2 + 1, -0.3);

            if (selectedPages.Length > 1)
            {
                selectedPages[1].ViewportWidth = 6;
                selectedPages[1].ViewportOrigin = new Point(-pageSize / 2, -0.3);
            }

            return true;
        }

        private bool isBroadsideAnimating = false;
        private bool ChangeBroadside(double oldBroadside, double newBroadside)
        {
            if (pagingDisabled)
                return false;

            var oldPage = (int)oldBroadside;
            var oldPagingFactor = oldBroadside - oldPage;

            var newPage = (int)newBroadside;
            var pagingFactor = newBroadside - newPage;

            if (newPage > this.BroadsidesCount || newPage < 0)
                return false;

            if (!isBroadsideAnimating)
            {
                AnimatePaging(newBroadside, oldBroadside);
                return false;
            }

            if (pagingFactor == 0)
            {
                this.FixPage(newPage);
                return true;
            }

            if (oldPage != newPage || oldPagingFactor == 0)
            {
                this.SelectedBroadside = newPage;
                this.CreatePageScreenshot();
                this.SelectedBroadside = newPage + 1;
            }

            this.PagingFactor = pagingFactor;

            return true;
        }

        private void AnimatePaging(double to, double? from = null)
        {
            if (to < 0 || to > BroadsidesCount || isBroadsideAnimating)
                return;

            isBroadsideAnimating = true;

            bdr.Effect = effect;

            DoubleAnimation anim = new DoubleAnimation()
            {
                From = from ?? this.Broadside,
                To = to,
                Duration = this.DefaultTransitionsTime
            };
            anim.Completed += (o, e) =>
                {
                    isBroadsideAnimating = false;
                    if (PagingFactor == 1)
                        pagingDisabled = false;
                    bdr.Effect = null;
                };

            effect.Size = 1 / pageSize * Zoom;

            Storyboard s = new Storyboard();
            s.Children.Add(anim);
            Storyboard.SetTarget(anim, this);
            Storyboard.SetTargetProperty(anim, new PropertyPath("Broadside"));
            s.Begin();
        }

        public int BroadsidesCount { get { return msiBook.SubImages.Count / 2 - 1; } }

        #endregion

        private bool pagingDisabled = false;

        public UIElement PrintSource
        {
            get
            {
                return new Image() { Source = new WriteableBitmap(bdr, null) };
            }
        }

        #region Zooming

        private bool ChangeZoom(double oldZoom, double newZoom)
        {
            if (isBroadsideAnimating)
                return false;

            if (newZoom < 1)
                return false;

            double zoomRatio = newZoom / oldZoom;
            pagingDisabled = true;
            var p = msiBook.ElementToLogicalPoint(new Point(msiBook.ActualWidth / 2, msiBook.ActualHeight / 2));

            msiBook.ZoomAboutLogicalPoint(zoomRatio, p.X, p.Y);

            return true;
        }

        private void book_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (e.Delta > 0) Zoom *= 1.1;
            else Zoom /= 1.1;
        }

        #endregion

        #region Move manipulations

        private bool isMouseLeftButtonPressed = false;
        private bool mouseMoved = false;
        private Point? prevMousePosition;
        private Point viewportOrigin;
        private void book_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            isMouseLeftButtonPressed = true;
            prevMousePosition = e.GetPosition(this.Parent as UIElement);
            viewportOrigin = msiBook.ViewportOrigin;
            Focus();
        }

        private void book_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (!mouseMoved)
            {
                if (e.GetPosition(this).X > msiBook.ActualWidth * (-msiBook.ViewportOrigin.X + 0.5) / msiBook.ViewportWidth)
                    Broadside++;
                else
                    Broadside--;
            }

            isMouseLeftButtonPressed = false;
            mouseMoved = false;
        }

        private void book_MouseMove(object sender, MouseEventArgs e)
        {
            if (!isMouseLeftButtonPressed || isBroadsideAnimating)
                return;

            mouseMoved = true;

            Point targetPoint = e.GetPosition(this.Parent as UIElement);

            if (prevMousePosition != null)
            {
                var currPosition = e.GetPosition(msiBook);
                var currPositionRelative = msiBook.ElementToLogicalPoint(currPosition);
                var prevPositionRelative = msiBook.ElementToLogicalPoint(prevMousePosition.Value);

                viewportOrigin = new Point(viewportOrigin.X - (currPositionRelative.X - prevPositionRelative.X), viewportOrigin.Y - (currPositionRelative.Y - prevPositionRelative.Y));
                pagingDisabled = true;
                msiBook.ViewportOrigin = viewportOrigin;
                prevMousePosition = currPosition;
            }

            prevMousePosition = e.GetPosition(this.Parent as UIElement);
            msiBook.UpdateLayout();
            CorrectViewportOrigin();
        }

        private void CorrectViewportOrigin()
        {
            return;
            double leftBound = -0.3 / Zoom;
            double topBound = -0.3 / Zoom;
            double rightBound = 1.0 - 0.7 / Zoom;
            double bottomBound = 1.0 * msiBook.ActualHeight / msiBook.ActualWidth - 0.3 / Zoom;
            if (msiBook.ViewportOrigin.X < leftBound)
            {
                msiBook.ViewportOrigin = new Point(leftBound, msiBook.ViewportOrigin.Y);
            }
            if (msiBook.ViewportOrigin.Y < topBound)
            {
                msiBook.ViewportOrigin = new Point(msiBook.ViewportOrigin.X, -topBound);
            }
            if (msiBook.ViewportOrigin.X > rightBound)
            {
                msiBook.ViewportOrigin = new Point(rightBound, msiBook.ViewportOrigin.Y);
            }
            if (msiBook.ViewportOrigin.Y > bottomBound)
            {
                msiBook.ViewportOrigin = new Point(msiBook.ViewportOrigin.X, bottomBound);
            }
        }

        #endregion

        private void msiBook_MotionFinished(object sender, RoutedEventArgs e)
        {
            pagingDisabled = false;
            effect.Center = (-msiBook.ViewportOrigin.X + 0.5) / msiBook.ViewportWidth;
        }

    }
}
